Skip to content

Releases: livestorejs/livestore

v0.3.0

22 May 16:56
6247e00
Compare
Choose a tag to compare

New features

  • New sync implementation (based on git-like push/pull semantics)

    • See Syncing docs page for more details
    • sync-cf backend: More reliable websocket connection handling
    • Configurable sync semantics when app starts (either skip initial sync or block with timeout)
  • New: Node adapter @livestore/adapter-node (experimental)

    • Note: Currently uses the @livestore/sqlite-wasm build but the plan is to move to a native SQLite build in the future to improve performance and reduce bundle size.
    • Still lacks a few devtools-related flows (e.g. graceful import/reset)
  • New: @livestore/sync-electric backend (experimental)

  • New: @livestore/adapter-expo now supports syncing (requires Expo 53 or later):

    const adapter = makePersistedAdapter({
      sync: { backend: makeCfSync({ url: `https://...` }) },
    })
  • New: Solid integration @livestore/solid (experimental)

    • Still very early stage and probably lacks some features. Feedback wanted!
    • Thank you to @kulshekhar for the initial implementation! (See PR #225)
    • There are is still a lot of work to be done - contributions welcome!

Breaking changes

  • Breaking: Renamed adapter packages:

    • @livestore/web now is @livestore/adapter-web
    • @livestore/expo now is @livestore/adapter-expo
  • Breaking: Removed @livestore/db-schema package and moved to @livestore/common/schema

  • Breaking: Renamed store.mutate to store.commit

    • Reason: Make it more clear that committing mutations is also syncing them across other clients
  • Breaking: Adjusted schema API

    • The new API aims to separate the schema into state and events
    • Mutations are now split up into event definitions and materializer functions

    Before:

    // mutations.ts
    import { Schema, defineMutation } from '@livestore/livestore'
    
    // Mutations are now split up into event definitions and materializer functions
    export const todoCreated = defineMutation('todoCreated',
      Schema.Struct({
        id: DbSchema.text(),
        text: DbSchema.text(),
      }),
      sql`INSERT INTO todos (id, text) VALUES (${id}, ${text})`,
    )
    
    // schema.ts
    import { DbSchema, makeSchema } from '@livestore/livestore'
    import * as mutations from './mutations.js'
    
    const todos = DbSchema.table('todos', {
      id: DbSchema.text({ primaryKey: true }),
      text: DbSchema.text(),
    })
    
    const uiState = DbSchema.table('uiState', {
      id: DbSchema.text({ primaryKey: true }),
      newTodoText: DbSchema.text(),
      filter: DbSchema.text({ }),
    }, {
      derivedMutations: { clientOnly: true }
    })
    
    const tables = { todos, uiState }
    const schema = makeSchema({ tables, mutations })

    After:

    // events.ts
    import { Events, Schema } from '@livestore/livestore'
    
    export const todoCreated = Events.synced({
      name: 'todoCreated',
      schema: Schema.Struct({ id: Schema.String, text: Schema.String, }),
    })
    
    // schema.ts
    import { State, Schema, makeSchema } from '@livestore/livestore'
    import * as events from './events.js'
    
    const todos = State.SQLite.table({
      name: 'todos',
      columns: {
        id: State.SQLite.text({ primaryKey: true }),
        text: State.SQLite.text(),
      }
    })
    
    // tables with `deriveMutations` are now called `clientDocuments`
    const uiState = State.SQLite.clientDocument({
      name: 'uiState',
      schema: Schema.Struct({
        newTodoText: Schema.String,
        filter: Schema.String,
      }),
    })
    
    const tables = { todos, uiState }
    
    // Materalizers let you materialize events into the state
    const materializers = State.SQLite.materializers(events, {
      'v1.TodoCreated': ({ id, text }) => todos.insert({ id, text }),
    })
    
    // Currently SQLite is the only supported state implementation but there might be more in the future (e.g. pure in-memory JS, DuckDB, ...)
    const state = State.SQLite.makeState({ tables, materializers })
    
    // Schema is now more clearly separated into state and events
    const schema = makeSchema({ state, events })
  • Breaking @livestore/react: Removed useScopedQuery in favour of useQuery. Migration example:

    // before
    const query$ = useScopedQuery(() => queryDb(tables.issues.query.where({ id: issueId }).first()), ['issue', issueId])
    
    // after
    const query$ = useQuery(queryDb(tables.issues.query.where({ id: issueId }).first(), { deps: `issue-${issueId}` }))
  • Breaking @livestore/adapter-web: Renamed makeAdapter to makePersistedAdapter

  • Breaking @livestore/adapter-expo: Renamed makeAdapter to makePersistedAdapter

  • Breaking: Renamed localOnly to clientOnly in table/mutation definitions.

  • Breaking: Renamed makeBackend to backend in sync options.

  • Breaking @livestore/react: useClientDocument now only works with for tables with client-only derived mutations.

  • Breaking: Instead of calling query$.run() / query$.runAndDestroy(), please use store.query(query$) instead.

  • Breaking: Removed store.__execute from Store.

  • Breaking: Removed globalReactivityGraph and explicit passing of reactivityGraph to queries.

  • Breaking: Removed persisted option from store.commit. This will be superceded by eventlog compaction in the future.

  • Breaking: The new syncing implementation required some changes to the storage format. The liveStoreStorageFormatVersion has been bumped to 3 which will create new database files.

  • Breaking: Moved queryGraphQL to @livestore/graphql and thus removing graphql from peer dependencies of @livestore/livestore.

  • Moved dev helper methods from e.g. store.__devDownloadDb() to store._dev.downloadDb()

  • Breaking @livestore/sync-cf: Renamed makeWsSync to makeCfSync

Notable improvements & fixes

  • Added support for write queries in the query builder

    table.query.insert({ id: '123', name: 'Alice' })
    table.query.insert({ id: '123', name: 'Alice' }).onConflict('id', 'ignore')
    table.query.insert({ id: '123', name: 'Alice' }).returning('id')
    table.query.update({ name: 'Bob' }).where({ id: '123' })
    table.query.delete().where({ id: '123' })
  • Introduced @livestore/peer-deps package to simplify dependency management for Livestore packages if you don't want to manually install all the peer dependencies yourself.

  • Improved documentation (still a lot of work to do here)

  • Shows a browser dialog when trying to close a tab/window with unsaved changes

  • The SQLite leader database now uses the WAL mode to improve performance and reliability. (Thanks @IGassmann for the contribution #259.)

  • Improve Otel tracing integration

  • Fix: The query builder now correctly handles IN and NOT IN where operations

  • Fix: LiveStore crashes when using reserved keywords as a column name (from) #245

Devtools

  • Changed devtools path from /_devtools.html to /_livestore
  • General connection stability improvements
  • Improved sync view:
    • See sync heads in real-time
    • Connect/disconnect button
  • Improved eventlog view:
    • Client-only mutations are now highlighted
    • Added clientId / sessionId columns
  • Grouped slow queries and live queries under new queries tab
  • Added SQLite query playground
  • Fix: Data browser now more clearly highlights selected table #239

Examples

  • Reworked the Linearlite React example. (Thanks @lukaswiesehan for the contribution #248.)
  • Adjusted mutation names to use past-tense
  • Added Otel to todomvc and todomvc-sync-cf example

Internal changes

  • Embraced git-style push/pull semantics to sync mutations across the system
  • Added node syncing integration tests
  • Got rid of the coordinator abstraction in favour of a clear separation between leader and client sessions
  • Renamed from EventId.local to EventSequenceNumber.client
  • Added @livestore/sqlite-wasm package which wraps @livestore/wa-sqlite and exposes web and Node.js compatible VFS implementations
  • New devtools protocol via webmesh
    • Should improve reliability of devtools connection (particularly during app reloads)
  • Large refactoring to share more code between adapters
  • Renamed SynchronousDatabase to SqliteDb
  • Upgrade to TypeScript 5.8
  • Upgraded dependencies
    • Now supports React 19
    • effect (needs to be 3.15.2 or higher)
    • @livestore/wa-sqlite (needs to be 1.0.5-dev.2)

v0.3.0-dev.54

22 May 15:23
6247e00
Compare
Choose a tag to compare
v0.3.0-dev.54 Pre-release
Pre-release

Release 0.3.0-dev.54 including Chrome Extension

v0.3.0-dev.53

20 May 20:33
6247e00
Compare
Choose a tag to compare
v0.3.0-dev.53 Pre-release
Pre-release

Release 0.3.0-dev.53 including Chrome Extension

v0.3.0-dev.52

20 May 14:21
6247e00
Compare
Choose a tag to compare
v0.3.0-dev.52 Pre-release
Pre-release

Release 0.3.0-dev.52 including Chrome Extension

v0.3.0-dev.51

20 May 13:18
6247e00
Compare
Choose a tag to compare
v0.3.0-dev.51 Pre-release
Pre-release

Release 0.3.0-dev.51 including Chrome Extension

v0.3.0-dev.50

14 May 08:37
6247e00
Compare
Choose a tag to compare
v0.3.0-dev.50 Pre-release
Pre-release

Release 0.3.0-dev.50 including Chrome Extension

v0.3.0-dev.49

10 May 09:20
6247e00
Compare
Choose a tag to compare
v0.3.0-dev.49 Pre-release
Pre-release

Release 0.3.0-dev.49 including Chrome Extension

v0.3.0-dev.48

10 May 08:10
6247e00
Compare
Choose a tag to compare
v0.3.0-dev.48 Pre-release
Pre-release

Release 0.3.0-dev.48 including Chrome Extension

v0.3.0-dev.47

06 May 08:06
6247e00
Compare
Choose a tag to compare
v0.3.0-dev.47 Pre-release
Pre-release

Release 0.3.0-dev.47 including Chrome Extension

v0.3.0-dev.46

05 May 07:58
6247e00
Compare
Choose a tag to compare
v0.3.0-dev.46 Pre-release
Pre-release

Release 0.3.0-dev.46 including Chrome Extension